/*
 * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xuexiang.xdlink.component.channel.impl.socket.client;

import android.text.TextUtils;

import com.xuexiang.xdlink.component.channel.IChannel;
import com.xuexiang.xdlink.component.channel.OnChannelListener;
import com.xuexiang.xdlink.component.channel.entity.ConnectStatusEnum;
import com.xuexiang.xdlink.component.channel.impl.AbstractDataReceiver;
import com.xuexiang.xdlink.logs.DCFLog;
import com.xuexiang.xdlink.util.DCFExecutors;
import com.xuexiang.xdlink.util.Utils;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * 长连接 Socket客户端通信通道
 *
 * @author xuexiang
 * @since 2018/8/19 下午11:39
 */
public class ClientSocketChannel extends AbstractClientSocketChannel {

    /**
     * 构造方法
     *
     * @param builder 构建者
     */
    public ClientSocketChannel(Builder builder) {
        super(builder.ip, builder.port, builder.timeout, builder.bufferSize, builder.isAutoSwitchThread);
    }

    @Override
    public void openChannel() {
        if (isOpen()) return;

        DCFExecutors.get().addTask(new Runnable() {
            @Override
            public void run() {
                connectSocket();
            }
        });
    }

    /**
     * 建立socket连接
     */
    @Override
    protected void connectSocket() {
        try {
            mSocket = createSocket();
            //阻塞的方法
            mSocket.connect(new InetSocketAddress(InetAddress.getByName(mIP), mPort), mTimeout);
            handleConnectResult(mSocket.isConnected());
        } catch (Exception e) {
            e.printStackTrace();
            DCFLog.e("Socket[client] connect to server failed!", e);
            handleConnectResult(false);
            Utils.closeIO(mSocket);
            mSocket = null;
        }
    }

    /**
     * 处理连接结果
     *
     * @param isConnectSuccess 是否连接成功
     */
    private void handleConnectResult(boolean isConnectSuccess) {
        if (isConnectSuccess) {
            DCFLog.i("Socket[client] connect to server success...");

            startAcceptData(new SocketDataReceiver(mSocket, getChannelListener()));

            if (getReOpenCount() > 0) {
                updateConnectStatus(ConnectStatusEnum.RECONNECT_SUCCESS);// 重新打开设备成功,发送命令
            } else {
                updateConnectStatus(ConnectStatusEnum.CONNECT_SUCCESS);// 打开设备成功,发送命令
            }
        } else {
            if (getReOpenCount() > 0) {
                updateConnectStatus(ConnectStatusEnum.RECONNECT_FAILED);
            } else {
                updateConnectStatus(ConnectStatusEnum.CONNECT_FAILED);
            }
        }
    }

    /**
     * 判断是否断开连接
     *
     * @return true :断开, false:没有
     */
    public boolean isServerClosed() {
        if (mSocket == null) {
            return true;
        }
        try {
            mSocket.sendUrgentData(0);// 发送1个字节的紧急数据，默认情况下，服务器端没有开启紧急数据处理，不影响正常通信
            return false;
        } catch (Exception e) {
            return true;
        }
    }

    @Override
    public void reOpenChannel() {
        super.reOpenChannel();
        closeChannel();
        openChannel();
    }

    /**
     * Socket数据接收器
     *
     * @author xuexiang
     * @since 2018/8/19 下午11:56
     */
    private class SocketDataReceiver extends AbstractDataReceiver {

        SocketDataReceiver(Socket socket, OnChannelListener listener) {
            super(socket, listener);
        }

        @Override
        public IChannel getChannel() {
            return ClientSocketChannel.this;
        }
    }

    //=============================构建者==============================//
    /**
     * 构建者
     */
    public static class Builder {
        /**
         * 需要连接的服务端IP地址
         */
        String ip;
        /**
         * 开放的端口号
         */
        int port = -1;
        /**
         * 连接的超时时间
         */
        int timeout;
        /**
         * 缓存大小
         */
        int bufferSize;
        /**
         * 是否自动切换线程
         */
        boolean isAutoSwitchThread;
        /**
         * 是否是短连接
         */
        boolean isShort;

        public Builder() {
            timeout = DEFAULT_CONNECT_TIMEOUT;
            bufferSize = DEFAULT_BUFFER_SIZE;
            isAutoSwitchThread = true;
            isShort = true;
        }

        public Builder setIp(String ip) {
            this.ip = ip;
            return this;
        }

        public Builder setPort(int port) {
            this.port = port;
            return this;
        }

        public Builder setTimeout(int timeout) {
            this.timeout = timeout;
            return this;
        }

        public Builder setAutoSwitchThread(boolean autoSwitchThread) {
            isAutoSwitchThread = autoSwitchThread;
            return this;
        }

        public Builder setBufferSize(int bufferSize) {
            this.bufferSize = bufferSize;
            return this;
        }

        public ClientSocketChannel build() {
            testArguments();
            return new ClientSocketChannel(this);
        }

        private void testArguments() {
            if (TextUtils.isEmpty(ip)) {
                throw new IllegalArgumentException("ClientSocketChannel IP address must be set！");
            }
            if (port == -1) {
                throw new IllegalArgumentException("ClientSocketChannel Port number must be set！");
            }
        }
    }



}
